module sdldee.video.surface;

import std.outofmemory;
import std.string;

import sdldee.c.sdl;
import sdldee.rwops;
import sdldee.exception;
import sdldee.video.pixelformat;
import sdldee.video.rectangle;
import sdldee.video.colour;
import sdldee.video.exception;


enum SurfaceFlag {
	// Available for SDL_CreateRGBSurface() or SDL_SetVideoMode()
	SWSURFACE = 0x00000000, // Surface is in system memory
	HWSURFACE = 0x00000001, // Surface is in video memory
	ASYNCBLIT = 0x00000004, // Use asynchronous blits if possible
	// Available for SDL_SetVideoMode()
	ANYFORMAT = 0x10000000, // Allow any video depth/pixel-format
	HWPALETTE = 0x20000000, // Surface has exclusive palette
	DOUBLEBUF = 0x40000000, // Set up double-buffered video mode
	FULLSCREEN = 0x80000000, // Surface is a full screen display
	OPENGL = 0x00000002, // Create an OpenGL rendering context
	OPENGLBLIT	= 0x0000000A, // Create an OpenGL rendering context and use it for blitting
	RESIZABLE = 0x00000010,	// This video mode may be resized
	NOFRAME = 0x00000020, // No window caption or edge frame
	// Used internally (read-only)
	HWACCEL = 0x00000100, // Blit uses hardware acceleration
	SRCCOLORKEY = 0x00001000, // Blit uses a source color key
	RLEACCELOK = 0x00002000, // Private flag
	RLEACCEL = 0x00004000, // Surface is RLE encoded
	SRCALPHA = 0x00010000, // Blit uses source alpha blending
	PREALLOC	= 0x01000000 //Surface uses preallocated memory
}

class Surface {
	private SDL_Surface* _surface;
	
	this(SDL_Surface* surface) {
		if(surface == null)
			throw new VideoException("Null surface");
		_surface = surface;
	}
	
	this(uint flags, int width, int height, int depth, uint rMask, uint gMask, uint bMask, uint aMask) {
		if((_surface = SDL_CreateRGBSurface(flags, width, height, depth, rMask, gMask, bMask, aMask)) == null)
			throw new OutOfMemoryException();
	}
	
	this(void* pixels, int width, int height, int depth, int pitch, uint rMask, uint gMask, uint bMask, uint aMask) {
		if((_surface = SDL_CreateRGBSurfaceFrom(pixels, width, height, depth, pitch, rMask, gMask, bMask, aMask)) == null)
			throw new OutOfMemoryException();
	}
	
	this(Rwops src) {
		if((_surface = SDL_LoadBMP_RW(src.crwops, false)) == null)
			throw new VideoException("Cannot load image");
	}
	
	this(char[] filename) {
		this(new Rwops(filename, "rb"));
	}
	
	~this() {
		SDL_FreeSurface(_surface);
	}
	
	public SDL_Surface* csurface() {
		return _surface;
	}
	
	public uint flags() {
		return _surface.flags;
	}
	
	public PixelFormat format() {
		return new PixelFormat(_surface.format);
	}
	
	public int w() {
		return _surface.w;
	}
	
	public int h() {
		return _surface.h;
	}
	
	public ushort pitch() {
		return _surface.pitch;
	}
	
	public void* pixels() {
		return _surface.pixels;
	}
	
	public Rectangle clipRectangle() {
		return new Rectangle(&_surface.clip_rect);
	}
	
	public bool setColours(Colour[] colours, int firstcolour) {
		return(SDL_SetColors(_surface, Colour.colourArrayToCArray(colours), firstcolour, colours.length) == 1); 
	}
	
	public bool setPalette(int flags, Colour[] colours, int firstcolour) {
		return(SDL_SetPalette(_surface, flags, Colour.colourArrayToCArray(colours), firstcolour, colours.length) == 1);
	}
	
	public bool mustLock() {
		return _surface.offset || ((_surface.flags & (SurfaceFlag.HWSURFACE | SurfaceFlag.ASYNCBLIT | SurfaceFlag.RLEACCEL)) != 0);
	}
	
	public bool lock() {
		return SDL_LockSurface(_surface) == 0;
	}
	
	public void unlock() {
		SDL_UnlockSurface(_surface);
	}
	
	public void saveBmp(Rwops dst) {
		if((SDL_SaveBMP_RW(_surface, dst.crwops, false)) < 0)
			throw new VideoException("Cannot save image");
	}
	
	public void saveBmp(char[] filename) {
		saveBmp(new Rwops(filename, "wb"));
	}
	
	public void setColourKey(uint flags, uint key) {
		if(SDL_SetColorKey(_surface, flags, key) < 0)
			throw new VideoException("Cannot set colour key");
	}
	
	public void setAlpha(uint flags, ubyte alpha) {
		if(SDL_SetAlpha(_surface, flags, alpha) < 0)
			throw new VideoException("Cannot set alpha");
	}
	
	public void clipRect(Rectangle rect) {
		SDL_SetClipRect(_surface, rect.crect);
	}
	
	public Rectangle clipRect() {
		Rectangle rect = new Rectangle();
		SDL_GetClipRect(_surface, rect.crect);
		return rect;
	}
	
	public Surface convert(PixelFormat format, uint flags) {
		return new Surface(SDL_ConvertSurface(_surface, format.cformat, flags));
	}
	
	public Surface displayFormat() {
		return new Surface(SDL_DisplayFormat(_surface));
	}
	
	public Surface displayFormatAlpha() {
		return new Surface(SDL_DisplayFormatAlpha(_surface));
	}
	
	public void blit(Surface src, Rectangle srcrect, Rectangle destrect) {
		if(SDL_UpperBlit(src.csurface, srcrect.crect, _surface, destrect.crect) < 0)
			throw new VideoException("Cannot blit surface");
	}
	
	public void fill(Rectangle rect, Colour colour) {
		if(SDL_FillRect(_surface, rect.crect, format.mapColour(colour)) < 0)
			throw new VideoException("Cannot fill rectangle");
	}
	
	public void fill(Colour colour) {
		if(SDL_FillRect(_surface, null, format.mapColour(colour)) < 0)
			throw new VideoException("Cannot fill rectangle");
	}
	
}
